'use strict';
/**
 * author：小小游 --- allen
 * date：2018.02.09
 * lastUpdateDate：2018.02.09
 * description：通过实例巩固变量解构赋值
 */
// 以前我们给变量赋值，只能直接指定值
var a = 1;
var b = 2;
var c = 3;
// result：1 2 3
console.log(a, b, c);

// 现在用解构赋值的写法就变得简单了，只要模式匹配上了就行了，如下：
// 注意数组是有顺序的
var [a, b, c] = [11, 22, 33];
// result：11 22 33
console.log(a, b, c);

// 现在用解构赋值的写法就变得简单了，只要模式匹配上了就行了，如下：
// 注意数组是有顺序的
var [b, a, c] = [11, 22, 33];
// result：22 11 33
console.log(a, b, c);

// 当然解构赋值还有嵌套比较复杂的写法，如下：
let [foo, [[bar], [baz]]] = [111, [[222], [333]]];
// result：111 222 333
console.log(foo, bar, baz);

// 当然解构赋值还有嵌套比较复杂的写法，如下：
let [,two,] = [,'second',];
// result：second
console.log(two);

// 当然解构赋值还有嵌套比较复杂的写法，如下：
let [x, y] = [4, 5, 6];
// result：4 5
console.log(x, y);

// 当然解构赋值还有嵌套比较复杂的写法，如下：
let [head, ...foot] = [1, 2, 3, 4];
// result：1 [2, 3, 4]
console.log(head, foot);

// 当然解构赋值还有嵌套比较复杂的写法，如下：
let [i1, i2, ...ix] = [6];
// result：6 undefined []
console.log(i1, i2, ix);

// 如果解构不成功，变量的值就等于undefined，如下：
var [foo2] = [];
// result：undefined
console.log(foo2);

// 如果解构不成功，变量的值就等于undefined，如下：
var [bar3, foo3] = [1000];
// result：1000 undefined
console.log(bar3, foo3);

/**
 * 另一种情况是不完全解构，即等号左边的模式，只匹配一部分的等号右边的数组。
 * 这种情况下，解构依然可以成功
 */
let [xx, yy] = [10000, 20000, 30000];
// result：10000 20000
console.log(xx, yy);

/**
 * 另一种情况是不完全解构，即等号左边的模式，只匹配一部分的等号右边的数组。
 * 这种情况下，解构依然可以成功
 */
let [aaa, [bbb], ccc] = [100000, [200000, 300000, 400000], 500000];
// result：100000 200000 500000
console.log(aaa, bbb, ccc);

// 如果等号的右边不是数组（或者严格地说，不是可遍历的结构），那么将会报错
/**
 * 上面的表达式都会报错，因为等号右边的值，要么转为对象以后不具备
 * Iterator接口(前五个表达式)，要么本身就不具备Iterator接口
 * (最后一个表达式)
*/
// let [awe] = 1;
// let [bnm] = false;
// let [cvb] = NaN;
// let [dfg] = undefined;
// let [ert] = null;
// let [fgh] = {};
// TypeError: Invalid attempt to destructure non-iterable instance
// 未捕获的错误类型，不确定是不是一个函数
// console.log(`
//     awe ---> ${awe},
//     bnm ---> ${bnm},
//     cvb ---> ${cvb},
//     dfg ---> ${dfg},
//     ert ---> ${ert},
//     fgh ---> ${fgh}
// `);

// 解构赋值不仅适用于var命令，也适用于let和const命令
var [v1, v2, ...vN] = [1, 2, [3, 4, 5]];
// result：1 2 [3, 4, 5]
console.log(v1,v2,vN);
let [v11, v22, ...vN2] = [11, 22, [33, 44, 55]];
// result：11 22 [33,44,55]
console.log(v11, v22, vN2);
const [v111, v222, ...vN222] = [111, 222, [333, 444, 555]];
// result：111 222 [333, 444, 555]
console.log(v111, v222, vN222);

// 对于Set结构，也可以使用数组的解构赋值
let [xcv, yui, zxc] = new Set(['a', 'b', 'c']);
// result：a b c
console.log(xcv,yui,zxc);

/**
 * 事实上，只要某种数据结构具有Iterator接口，都可以采用数组形式的解构赋值,
 * 上面代码中，fibs是一个Generator函数，原生具有Iterator接口。
 * 解构赋值会依次从这个接口获取值
 */
function * fibs () {
    var a = 0;
    var b = 1;
    while(true){
        yield a;
        [a, b] = [b, a + b];
    }
}

var [first, second, third, fourth, fifth, sixth] = fibs();
// result：0 1 1 2 3 5
console.log(first, second, third, fourth, fifth, sixth);

// 解构赋值允许指定默认值
var [foo111 = false] = [];
// result：false
console.log(foo111);

// 解构赋值允许指定默认值
var [xxxxx, yyyyy = 'b'] = ['a'];
// result：a b
console.log(xxxxx, yyyyy);

// 解构赋值允许指定默认值
var [j, k = 'bbb'] = ['aaa', undefined];
// result：aaa bbb
console.log(j, k);

/**
 * 注意，ES6内部使用严格相等运算符（===），判断一个位置是否有值。所以，
 * 如果一个数组成员不严格等于undefined，默认值是不会生效的
 */
var [xiaoyou = 1] = [undefined];
// result：1
console.log(xiaoyou);

/**
 * 上面代码中，如果一个数组成员是null，默认值就不会生效，
 * 因为null不严格等于undefined
 */
var [youyou = 1] = [null];
// result：null
console.log(youyou);

/**
 * 如果默认值是一个表达式，那么这个表达式是惰性求值的，
 * 即只有在用到的时候，才会求值
 */
function fn () {
    console.log('aaa');
}
let [xixi = fn ()] = [1];
// result：1
console.log(xixi);

function fn1 () {
    console.log('aaa');
}
let [xixixi = fn1 ()] = [1];
// result：aaa
fn1 ();
// result：1
console.log(xixixi);

/**
 * 第一段代码中，因为x能取到值，所以函数f根本不会执行。
 * 上面的代码其实等价于下面的代码
 */
function fn2 () {
    console.log('aaa');
}
let xxyouyou;
if ( [1][0] === undefined ) {
    xxyouyou = fn2 ();
} else {
    xxyouyou = [1][0];
}
// result：aaa
fn2 ();
// result：1
console.log(xxyouyou);

// 默认值可以引用解构赋值的其他变量，但该变量必须已经声明
let [abcyou = 1, bcayou = abcyou] = [];
// result：1 1
console.log(abcyou, bcayou);

// 默认值可以引用解构赋值的其他变量，但该变量必须已经声明
let [aoo = 1, boo = aoo] = [2];
// result：2 2
console.log(aoo, boo);

// 默认值可以引用解构赋值的其他变量，但该变量必须已经声明
let [app = 1, bpp = app] = [2, 3];
// result：2 3
console.log(app, bpp);

/**
 * 默认值可以引用解构赋值的其他变量，但该变量必须已经声明，
 * 这个表达式之所以会报错，是因为a用到默认值b时，b还没有声明
 */
let [ajj = bjj, bjj = 1] = [];
// result：undefined 1
console.log(ajj, bjj);

/**
 * 对象的解构赋值
 * 解构不仅可以用于数组，还可以用于对象(json)
 */
var { ang, bng } = { 'ang' : 'apple', 'bng' : 'banana' };
// result：apple banana
console.log(ang, bng);

/**
 * 对象的解构与数组有一个重要的不同。数组的元素是按次序排列的，
 * 变量的取值由它的位置决定；而对象的属性没有次序，变量必须与
 * 属性同名，才能取到正确的值
 */
var { bppg, appg } = { 'appg' : 'apple', 'bppg' : 'banana' };
// result：apple banana
console.log(appg, bppg);

// 变量没有对应的同名属性，导致取不到值，最后等于undefined
var { cppg } = { 'a' : 'apple', 'b' : 'banana' };
// result：undefined
console.log(cppg);

// 如果变量名与属性名不一致，必须写成下面这样
// a ---> c，那么此时c就是apple，而不是undefined了
var { ahhg : chhg } = { 'ahhg' : 'apple', 'bhhg' : 'banana' };
// result：apple
console.log(chhg);

// 如果变量名与属性名不一致，必须写成下面这样
let obj = { 'first' : 'hello', 'last' : 'world' };
// first ---> f，那么此时f就是first，而不是undefined了
let {first : f, last } = obj;
// result：hello world
console.log(f, last);

// 这实际上说明，对象的解构赋值是下面形式的简写
// foo ---> f，那么此时f就是foo，而不是undefined了
// bar ---> b，那么此时b就是bbb，而不是undefined了
var { foo : fyug, bar : byug} = {'foo' : 'fff', 'bar' : 'bbb'};
// result：fff bbb
console.log(fyug, byug);

/**
* 1.也就是说，对象的解构赋值的内部机制，是先找到同名属性，然后再赋给对应的变量。
* 真正被赋值的是后者，而不是前者
* 2.v是匹配的模式，n才是变量。真正被赋值的是变量n，而不是模式v。
* 注意，采用这种写法时，变量的声明和赋值是一体的
*/
// v ---> n，那么此时n就是vue，而不是undefined了
var {v : n} = { 'v' : 'vue', 'r' : 'react' };
// result：vue
console.log(n);
// result：v是模式不能被输出会报错 v is not defined
// console.log(v);
// result：同样为定义会报错 r is not defined
// console.log(r);

// 对于let和const来说，变量不能重新声明，所以一旦赋值的变量以前声明过，就会报错
/**
 * 解构赋值的变量都会重新声明，所以报错了。不过，因为var命令允许重新声明，所以这个
 * 错误只会在使用let和const命令时出现。如果没有第二个let命令，上面的代码就不会报错
 */
// let foo12345;
// let foo12345 = { 'foo12345' : 1 };
// result：Uncaught SyntaxError: Identifier 'foo' has already been declared
// console.log(foo12345);

// let b;
/**
 * a ---> b，那么此时b就是1，而不是undefined了(但是由于let不能重复声明变量)
 * 所以就报错了 Uncaught SyntaxError: Identifier 'b' has already been declared
 */
// let { a : b } = { a : 1, b : 2 };
// result：Uncaught SyntaxError: Identifier 'b' has already been declared
// console.log(b);

/**
 * 1.解构赋值的变量都会重新声明，所以报错了。不过，因为var命令允许重新声明，所以这个错误
 *   只会在使用let和const命令时出现。如果没有第二个let命令，上面的代码就不会报错
 * 2.let命令下面一行的圆括号是必须的，否则会报错。因为解析器会将起首的大括号，
 *   理解成一个代码块，而不是赋值语句。
 */
let fooii;
({ fooii } = { 'fooii' : 1 });
// result：1
console.log(fooii);

let bazzz;
// bar ---> baz，那么此时baz就是111，而不是undefined了
({ bar : bazzz } = { 'bar' : 111 });
// result：111
console.log(bazzz);

/**
 * 1.和数组一样，解构也可以用于嵌套结构的对象
 * 2.注意，这时p是模式，不是变量，因此不会被赋值
 */
var obj555 = {
    p : [
        'hello',
        {
            'w' : 'world'
        }
    ]
};
var {p : [h, { w }]} = obj555;
// result：hello world
console.log(h, w);

// 下面代码中，只有line、column是变量，loc和start都是模式，不会被赋值
var node = {
    'loc' : {
        'start' : {
            'line' : 1,
            'column' : 5
        }
    }
};
var { loc : { start : { line, column }}} = node;
// result：1 5
console.log(line, column);
/**
 * loc和start都是模式不能被输出，会报错loc or start is not defined
 */
// console.log(loc);
// console.log(start);

/**
 * 下面是嵌套赋值的例子
 */
let obj666 = {};
let arr666 = [];
// foo ---> obj666.prop，那么此时obj666就是Object {prop:123}，而不是undefined了
// bar ---> arr666，那么此时arr666[0]就是true，而不是undefined了
({ foo : obj666.prop, bar : arr666[0] } = { 'foo': 123, 'bar': true});
// result：{ 'prop' : 123 }
console.log(obj666);
// result：true
console.log(arr666);

/**
 * 对象的解构也可以指定默认值
 */
var { xyuz = 3 } = { 'xyuz' : undefined };
// result：3
console.log(xyuz);

var { xouy = 3 } = { 'xouy' : null };
// result：null
console.log(xouy);

var { x110, y110 = 5 } = { 'x110' : 1 };
// result：1 5
console.log(x110, y110);

// xxyy ---> yyxx，那么此时y给予了默认值为5，而不是undefined了
// x变成了模式了，所以此时输出变量的时候会报错
var { xxyy : yyxx = 5 } = {};
// result： 5
console.log(yyxx);
/**
 * xxyy是模式，不能被输出会报错 xxyy is not defined
 */
// console.log(xxyy);

var { xuuu : yuuu = 5 } = { 'xuuu' : 10 };
// result：10
console.log(yuuu);
/**
 * xuuu是模式，不能被输出会报错 xuuu is not defined
 */
// console.log(xuuu);

var {
    'message' : msg = 'Hello World!'
}
=
{
    'message' : '我的英文名就是allen'
};
console.log(msg);
